/*
 * pgp-encrypt.c
 *	  OpenPGP 加密。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of条件和以下免责声明。
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of条件和以下免责声明在文档和/或其他提供的材料
 * 与分发一起。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 或后果
 * 损害（包括但不限于替代商品或服务的采购；丧失使用、数据或利润；或业务中断）
 * 无论因何原因以及在任何责任理论下，无论是合同、严格
 * 责任或侵权行为（包括疏忽或其他）以任何方式产生
 * 出于对本软件的使用，即使在被告知有可能发生
 * 这样的损害。
 *
 * contrib/pgcrypto/pgp-encrypt.c
 */

#include "postgres.h"

#include <time.h>

#include "mbuf.h"
#include "pgp.h"
#include "px.h"

#define MDC_DIGEST_LEN 20
#define STREAM_ID 0xE0
#define STREAM_BLOCK_SHIFT	14

static uint8 * fc_render_newlen(uint8 *fc_h, int fc_len)
{
	if (fc_len <= 191)
	{
		*fc_h++ = fc_len & 255;
	}
	else if (fc_len > 191 && fc_len <= 8383)
	{
		*fc_h++ = ((fc_len - 192) >> 8) + 192;
		*fc_h++ = (fc_len - 192) & 255;
	}
	else
	{
		*fc_h++ = 255;
		*fc_h++ = (fc_len >> 24) & 255;
		*fc_h++ = (fc_len >> 16) & 255;
		*fc_h++ = (fc_len >> 8) & 255;
		*fc_h++ = fc_len & 255;
	}
	return fc_h;
}

static int fc_write_tag_only(PushFilter *fc_dst, int fc_tag)
{
	uint8		fc_hdr = 0xC0 | fc_tag;

	return pushf_write(fc_dst, &fc_hdr, 1);
}

static int fc_write_normal_header(PushFilter *fc_dst, int fc_tag, int fc_len)
{
	uint8		fc_hdr[8];
	uint8	   *fc_h = fc_hdr;

	*fc_h++ = 0xC0 | fc_tag;
	fc_h = fc_render_newlen(fc_h, fc_len);
	return pushf_write(fc_dst, fc_hdr, fc_h - fc_hdr);
}


/*
 * MAC writer
 */

static int fc_mdc_init(PushFilter *fc_dst, void *fc_init_arg, void **fc_priv_p)
{
	int			fc_res;
	PX_MD	   *fc_md;

	fc_res = pgp_load_digest(PGP_DIGEST_SHA1, &fc_md);
	if (fc_res < 0)
		return fc_res;

	*fc_priv_p = fc_md;
	return 0;
}

static int fc_mdc_write(PushFilter *fc_dst, void *fc_priv, const uint8 *fc_data, int fc_len)
{
	PX_MD	   *fc_md = fc_priv;

	px_md_update(fc_md, fc_data, fc_len);
	return pushf_write(fc_dst, fc_data, fc_len);
}

static int fc_mdc_flush(PushFilter *fc_dst, void *fc_priv)
{
	int			fc_res;
	uint8		fc_pkt[2 + MDC_DIGEST_LEN];
	PX_MD	   *fc_md = fc_priv;

	/*
	 * 创建 mdc pkt
	 */
	fc_pkt[0] = 0xD3;
	fc_pkt[1] = 0x14;				/* MDC_DIGEST_LEN */
	px_md_update(fc_md, fc_pkt, 2);
	px_md_finish(fc_md, fc_pkt + 2);

	fc_res = pushf_write(fc_dst, fc_pkt, 2 + MDC_DIGEST_LEN);
	px_memset(fc_pkt, 0, 2 + MDC_DIGEST_LEN);
	return fc_res;
}

static void fc_mdc_free(void *fc_priv)
{
	PX_MD	   *fc_md = fc_priv;

	px_md_free(fc_md);
}

static const PushFilterOps mdc_filter = {
	fc_mdc_init, fc_mdc_write, fc_mdc_flush, fc_mdc_free
};


/*
 * 加密 pkt 写入器
 */
#define ENCBUF 8192
struct EncStat
{
	PGP_CFB    *ciph;
	uint8		buf[ENCBUF];
};

static int fc_encrypt_init(PushFilter *fc_next, void *fc_init_arg, void **fc_priv_p)
{
	struct EncStat *fc_st;
	PGP_Context *fc_ctx = fc_init_arg;
	PGP_CFB    *fc_ciph;
	int			fc_resync = 1;
	int			fc_res;

	/* 我们是否使用更新的包格式？ */
	if (fc_ctx->disable_mdc == 0)
	{
		uint8		fc_ver = 1;

		fc_resync = 0;
		fc_res = pushf_write(fc_next, &fc_ver, 1);
		if (fc_res < 0)
			return fc_res;
	}
	fc_res = pgp_cfb_create(&fc_ciph, fc_ctx->cipher_algo,
						 fc_ctx->sess_key, fc_ctx->sess_key_len, fc_resync, NULL);
	if (fc_res < 0)
		return fc_res;

	fc_st = palloc0(sizeof(*fc_st));
	fc_st->ciph = fc_ciph;

	*fc_priv_p = fc_st;
	return ENCBUF;
}

static int fc_encrypt_process(PushFilter *fc_next, void *fc_priv, const uint8 *fc_data, int fc_len)
{
	int			fc_res;
	struct EncStat *fc_st = fc_priv;
	int			fc_avail = fc_len;

	while (fc_avail > 0)
	{
		int			fc_tmplen = fc_avail > ENCBUF ? ENCBUF : fc_avail;

		fc_res = pgp_cfb_encrypt(fc_st->ciph, fc_data, fc_tmplen, fc_st->buf);
		if (fc_res < 0)
			return fc_res;

		fc_res = pushf_write(fc_next, fc_st->buf, fc_tmplen);
		if (fc_res < 0)
			return fc_res;

		fc_data += fc_tmplen;
		fc_avail -= fc_tmplen;
	}
	return 0;
}

static void fc_encrypt_free(void *fc_priv)
{
	struct EncStat *fc_st = fc_priv;

	if (fc_st->ciph)
		pgp_cfb_free(fc_st->ciph);
	px_memset(fc_st, 0, sizeof(*fc_st));
	pfree(fc_st);
}

static const PushFilterOps encrypt_filter = {
	fc_encrypt_init, fc_encrypt_process, NULL, fc_encrypt_free
};

/*
 * 写入可流式处理的 pkts
 */

struct PktStreamStat
{
	int			final_done;
	int			pkt_block;
};

static int fc_pkt_stream_init(PushFilter *fc_next, void *fc_init_arg, void **fc_priv_p)
{
	struct PktStreamStat *fc_st;

	fc_st = palloc(sizeof(*fc_st));
	fc_st->final_done = 0;
	fc_st->pkt_block = 1 << STREAM_BLOCK_SHIFT;
	*fc_priv_p = fc_st;

	return fc_st->pkt_block;
}

static int fc_pkt_stream_process(PushFilter *fc_next, void *fc_priv, const uint8 *fc_data, int fc_len)
{
	int			fc_res;
	uint8		fc_hdr[8];
	uint8	   *fc_h = fc_hdr;
	struct PktStreamStat *fc_st = fc_priv;

	if (fc_st->final_done)
		return PXE_BUG;

	if (fc_len == fc_st->pkt_block)
		*fc_h++ = STREAM_ID | STREAM_BLOCK_SHIFT;
	else
	{
		fc_h = fc_render_newlen(fc_h, fc_len);
		fc_st->final_done = 1;
	}

	fc_res = pushf_write(fc_next, fc_hdr, fc_h - fc_hdr);
	if (fc_res < 0)
		return fc_res;

	return pushf_write(fc_next, fc_data, fc_len);
}

static int fc_pkt_stream_flush(PushFilter *fc_next, void *fc_priv)
{
	int			fc_res;
	uint8		fc_hdr[8];
	uint8	   *fc_h = fc_hdr;
	struct PktStreamStat *fc_st = fc_priv;

	/* 流必须以正常包结束。 */
	if (!fc_st->final_done)
	{
		fc_h = fc_render_newlen(fc_h, 0);
		fc_res = pushf_write(fc_next, fc_hdr, fc_h - fc_hdr);
		if (fc_res < 0)
			return fc_res;
		fc_st->final_done = 1;
	}
	return 0;
}

static void fc_pkt_stream_free(void *fc_priv)
{
	struct PktStreamStat *fc_st = fc_priv;

	px_memset(fc_st, 0, sizeof(*fc_st));
	pfree(fc_st);
}

static const PushFilterOps pkt_stream_filter = {
	fc_pkt_stream_init, fc_pkt_stream_process, fc_pkt_stream_flush, fc_pkt_stream_free
};

int pgp_create_pkt_writer(PushFilter *fc_dst, int fc_tag, PushFilter **fc_res_p)
{
	int			fc_res;

	fc_res = fc_write_tag_only(fc_dst, fc_tag);
	if (fc_res < 0)
		return fc_res;

	return pushf_create(fc_res_p, &pkt_stream_filter, NULL, fc_dst);
}

/*
 * 文本转换过滤器
 */

static int fc_crlf_process(PushFilter *fc_dst, void *fc_priv, const uint8 *fc_data, int fc_len)
{
	const uint8 *fc_data_end = fc_data + fc_len;
	const uint8 *fc_p2,
			   *fc_p1 = fc_data;
	int			fc_line_len;
	static const uint8 fc_crlf[] = {'\r', '\n'};
	int			fc_res = 0;

	while (fc_p1 < fc_data_end)
	{
		fc_p2 = memchr(fc_p1, '\n', fc_data_end - fc_p1);
		if (fc_p2 == NULL)
			fc_p2 = fc_data_end;

		fc_line_len = fc_p2 - fc_p1;

		/* 写入数据 */
		fc_res = 0;
		if (fc_line_len > 0)
		{
			fc_res = pushf_write(fc_dst, fc_p1, fc_line_len);
			if (fc_res < 0)
				break;
			fc_p1 += fc_line_len;
		}

		/* 写入 crlf */
		while (fc_p1 < fc_data_end && *fc_p1 == '\n')
		{
			fc_res = pushf_write(fc_dst, fc_crlf, 2);
			if (fc_res < 0)
				break;
			fc_p1++;
		}
	}
	return fc_res;
}

static const PushFilterOps crlf_filter = {
	NULL, fc_crlf_process, NULL, NULL
};

/*
 * 初始化字面数据包
 */
static int fc_init_litdata_packet(PushFilter **fc_pf_res, PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	int			fc_res;
	int			fc_hdrlen;
	uint8		fc_hdr[6];
	uint32		fc_t;
	PushFilter *fc_pkt;
	int			fc_type;

	/*
	 * 创建头部
	 */

	if (fc_ctx->text_mode)
		fc_type = fc_ctx->unicode_mode ? 'u' : 't';
	else
		fc_type = 'b';

	/*
	 * 将创建时间存入数据包。目标是尽可能减少已知字节数。
	 */
	fc_t = (uint32) time(NULL);

	fc_hdr[0] = fc_type;
	fc_hdr[1] = 0;
	fc_hdr[2] = (fc_t >> 24) & 255;
	fc_hdr[3] = (fc_t >> 16) & 255;
	fc_hdr[4] = (fc_t >> 8) & 255;
	fc_hdr[5] = fc_t & 255;
	fc_hdrlen = 6;

	fc_res = fc_write_tag_only(fc_dst, PGP_PKT_LITERAL_DATA);
	if (fc_res < 0)
		return fc_res;

	fc_res = pushf_create(&fc_pkt, &pkt_stream_filter, fc_ctx, fc_dst);
	if (fc_res < 0)
		return fc_res;

	fc_res = pushf_write(fc_pkt, fc_hdr, fc_hdrlen);
	if (fc_res < 0)
	{
		pushf_free(fc_pkt);
		return fc_res;
	}

	*fc_pf_res = fc_pkt;
	return 0;
}

/*
 * 初始化压缩过滤器
 */
static int fc_init_compress(PushFilter **fc_pf_res, PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	int			fc_res;
	uint8		fc_type = fc_ctx->compress_algo;
	PushFilter *fc_pkt;

	fc_res = fc_write_tag_only(fc_dst, PGP_PKT_COMPRESSED_DATA);
	if (fc_res < 0)
		return fc_res;

	fc_res = pushf_create(&fc_pkt, &pkt_stream_filter, fc_ctx, fc_dst);
	if (fc_res < 0)
		return fc_res;

	fc_res = pushf_write(fc_pkt, &fc_type, 1);
	if (fc_res >= 0)
		fc_res = pgp_compress_filter(fc_pf_res, fc_ctx, fc_pkt);

	if (fc_res < 0)
		pushf_free(fc_pkt);

	return fc_res;
}

/*
 * 初始化 encdata 数据包
 */
static int fc_init_encdata_packet(PushFilter **fc_pf_res, PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	int			fc_res;
	int			fc_tag;

	if (fc_ctx->disable_mdc)
		fc_tag = PGP_PKT_SYMENCRYPTED_DATA;
	else
		fc_tag = PGP_PKT_SYMENCRYPTED_DATA_MDC;

	fc_res = fc_write_tag_only(fc_dst, fc_tag);
	if (fc_res < 0)
		return fc_res;

	return pushf_create(fc_pf_res, &pkt_stream_filter, fc_ctx, fc_dst);
}

/*
 * 写入前缀
 */
static int fc_write_prefix(PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	uint8		fc_prefix[PGP_MAX_BLOCK + 2];
	int			fc_res,
				fc_bs;

	fc_bs = pgp_get_cipher_block_size(fc_ctx->cipher_algo);
	if (!pg_strong_random(fc_prefix, fc_bs))
		return PXE_NO_RANDOM;

	fc_prefix[fc_bs + 0] = fc_prefix[fc_bs - 2];
	fc_prefix[fc_bs + 1] = fc_prefix[fc_bs - 1];

	fc_res = pushf_write(fc_dst, fc_prefix, fc_bs + 2);
	px_memset(fc_prefix, 0, fc_bs + 2);
	return fc_res < 0 ? fc_res : 0;
}

/*
 * 写入对称加密的会话密钥数据包
 */

static int fc_symencrypt_sesskey(PGP_Context *fc_ctx, uint8 *fc_dst)
{
	int			fc_res;
	PGP_CFB    *fc_cfb;
	uint8		fc_algo = fc_ctx->cipher_algo;

	fc_res = pgp_cfb_create(&fc_cfb, fc_ctx->s2k_cipher_algo,
						 fc_ctx->s2k.key, fc_ctx->s2k.key_len, 0, NULL);
	if (fc_res < 0)
		return fc_res;

	pgp_cfb_encrypt(fc_cfb, &fc_algo, 1, fc_dst);
	pgp_cfb_encrypt(fc_cfb, fc_ctx->sess_key, fc_ctx->sess_key_len, fc_dst + 1);

	pgp_cfb_free(fc_cfb);
	return fc_ctx->sess_key_len + 1;
}

/* 5.3: 对称密钥加密会话密钥 */
static int fc_write_symenc_sesskey(PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	uint8		fc_pkt[256];
	int			fc_pktlen;
	int			fc_res;
	uint8	   *fc_p = fc_pkt;

	*fc_p++ = 4;					/* 5.3 - 版本号 */
	*fc_p++ = fc_ctx->s2k_cipher_algo;

	*fc_p++ = fc_ctx->s2k.mode;
	*fc_p++ = fc_ctx->s2k.digest_algo;
	if (fc_ctx->s2k.mode > 0)
	{
		memcpy(fc_p, fc_ctx->s2k.salt, 8);
		fc_p += 8;
	}
	if (fc_ctx->s2k.mode == 3)
		*fc_p++ = fc_ctx->s2k.iter;

	if (fc_ctx->use_sess_key)
	{
		fc_res = fc_symencrypt_sesskey(fc_ctx, fc_p);
		if (fc_res < 0)
			return fc_res;
		fc_p += fc_res;
	}

	fc_pktlen = fc_p - fc_pkt;
	fc_res = fc_write_normal_header(fc_dst, PGP_PKT_SYMENCRYPTED_SESSKEY, fc_pktlen);
	if (fc_res >= 0)
		fc_res = pushf_write(fc_dst, fc_pkt, fc_pktlen);

	px_memset(fc_pkt, 0, fc_pktlen);
	return fc_res;
}

/*
 * 密钥设置
 */
static int fc_init_s2k_key(PGP_Context *fc_ctx)
{
	int			fc_res;

	if (fc_ctx->s2k_cipher_algo < 0)
		fc_ctx->s2k_cipher_algo = fc_ctx->cipher_algo;

	fc_res = pgp_s2k_fill(&fc_ctx->s2k, fc_ctx->s2k_mode, fc_ctx->s2k_digest_algo, fc_ctx->s2k_count);
	if (fc_res < 0)
		return fc_res;

	return pgp_s2k_process(&fc_ctx->s2k, fc_ctx->s2k_cipher_algo,
						   fc_ctx->sym_key, fc_ctx->sym_key_len);
}

static int fc_init_sess_key(PGP_Context *fc_ctx)
{
	if (fc_ctx->use_sess_key || fc_ctx->pub_key)
	{
		fc_ctx->sess_key_len = pgp_get_cipher_key_size(fc_ctx->cipher_algo);
		if (!pg_strong_random(fc_ctx->sess_key, fc_ctx->sess_key_len))
			return PXE_NO_RANDOM;
	}
	else
	{
		fc_ctx->sess_key_len = fc_ctx->s2k.key_len;
		memcpy(fc_ctx->sess_key, fc_ctx->s2k.key, fc_ctx->s2k.key_len);
	}

	return 0;
}

/*
 * 组合
 */
int pgp_encrypt(PGP_Context *fc_ctx, MBuf *fc_src, MBuf *fc_dst)
{
	int			fc_res;
	int			fc_len;
	uint8	   *fc_buf;
	PushFilter *fc_pf,
			   *fc_pf_tmp;

	/*
	 * 我们有任何密钥吗
	 */
	if (!fc_ctx->sym_key && !fc_ctx->pub_key)
		return PXE_ARGUMENT_ERROR;

	/* MBuf 写入器 */
	fc_res = pushf_create_mbuf_writer(&fc_pf, fc_dst);
	if (fc_res < 0)
		goto out;

	/*
	 * 初始化 sym_key
	 */
	if (fc_ctx->sym_key)
	{
		fc_res = fc_init_s2k_key(fc_ctx);
		if (fc_res < 0)
			goto out;
	}

	fc_res = fc_init_sess_key(fc_ctx);
	if (fc_res < 0)
		goto out;

	/*
	 * 写入 keypkt
	 */
	if (fc_ctx->pub_key)
		fc_res = pgp_write_pubenc_sesskey(fc_ctx, fc_pf);
	else
		fc_res = fc_write_symenc_sesskey(fc_ctx, fc_pf);
	if (fc_res < 0)
		goto out;

	/* 加密数据 pkt */
	fc_res = fc_init_encdata_packet(&fc_pf_tmp, fc_ctx, fc_pf);
	if (fc_res < 0)
		goto out;
	fc_pf = fc_pf_tmp;

	/* 加密器 */
	fc_res = pushf_create(&fc_pf_tmp, &encrypt_filter, fc_ctx, fc_pf);
	if (fc_res < 0)
		goto out;
	fc_pf = fc_pf_tmp;

	/* 哈希器 */
	if (fc_ctx->disable_mdc == 0)
	{
		fc_res = pushf_create(&fc_pf_tmp, &mdc_filter, fc_ctx, fc_pf);
		if (fc_res < 0)
			goto out;
		fc_pf = fc_pf_tmp;
	}

	/* 前缀 */
	fc_res = fc_write_prefix(fc_ctx, fc_pf);
	if (fc_res < 0)
		goto out;

	/* 压缩器 */
	if (fc_ctx->compress_algo > 0 && fc_ctx->compress_level > 0)
	{
		fc_res = fc_init_compress(&fc_pf_tmp, fc_ctx, fc_pf);
		if (fc_res < 0)
			goto out;
		fc_pf = fc_pf_tmp;
	}

	/* 数据流处理器 */
	fc_res = fc_init_litdata_packet(&fc_pf_tmp, fc_ctx, fc_pf);
	if (fc_res < 0)
		goto out;
	fc_pf = fc_pf_tmp;


	/* 文本转换？ */
	if (fc_ctx->text_mode && fc_ctx->convert_crlf)
	{
		fc_res = pushf_create(&fc_pf_tmp, &crlf_filter, fc_ctx, fc_pf);
		if (fc_res < 0)
			goto out;
		fc_pf = fc_pf_tmp;
	}

	/*
	 * 链接完成
	 */

	fc_len = mbuf_grab(fc_src, mbuf_avail(fc_src), &fc_buf);
	fc_res = pushf_write(fc_pf, fc_buf, fc_len);
	if (fc_res >= 0)
		fc_res = pushf_flush(fc_pf);
out:
	pushf_free_all(fc_pf);
	return fc_res;
}
